/**
 *    Copyright 2017-2018 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package com.econage.eccm.test.base.mybatis.support;

import com.econage.eccm.test.base.mybatis.adaptation.MybatisConfiguration;
import com.econage.eccm.test.base.mybatis.adaptation.MybatisGlobalAssistant;
import com.econage.eccm.test.base.mybatis.adaptation.MybatisXMLConfigBuilder;
import com.econage.eccm.test.base.mybatis.enums.DBType;
import com.econage.eccm.test.base.mybatis.uid.dbincrementer.OracleKeyGenerator;
import com.econage.eccm.test.base.mybatis.plugins.pagination.PaginationInterceptor;
import com.google.common.base.Preconditions;
import com.google.common.reflect.Reflection;
import com.google.common.util.concurrent.RateLimiter;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.type.JdbcType;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.Reader;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

public class MybatisWebContext {
    private static final Log LOGGER = LogFactory.getLog(MybatisWebContext.class);

    private volatile static SqlSession sqlSessionTemplate;

    private static RateLimiter rateLimiter = RateLimiter.create(1,30, TimeUnit.SECONDS);
    public static void checkSqlSessionTemplate(){
        if(sqlSessionTemplate==null){
            if(!rateLimiter.tryAcquire()){
                throw new IllegalStateException("操作过于频繁，请30秒后重试!");
            }
            synchronized (MybatisWebContext.class){
                if(sqlSessionTemplate==null){
                    Reader reader = null;
                    try {
                        reader = Resources.getResourceAsReader("mybatis-config.xml");
                        MybatisXMLConfigBuilder configBuilder = new MybatisXMLConfigBuilder(reader);
                        configBuilder.parse();
                        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configBuilder.getConfiguration());
                        sqlSessionTemplate = new MybatisSqlSessionTemplate(sqlSessionFactory,configBuilder.getConfiguration().getDefaultExecutorType());
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally{
                        if(reader!=null){
                            try { reader.close(); } catch (IOException e) { }
                        }
                    }
                }
            }
        }
    }



    //-------------应用常规操作
    public static <T> T getMapper(Class<T> mapperCls){
        checkSqlSessionTemplate();
        return sqlSessionTemplate.getMapper(mapperCls);
    }

    public static void commit(){
        checkSqlSessionTemplate();
        sqlSessionTemplate.commit();
    }

    public static void commit(boolean force){
        checkSqlSessionTemplate();
        sqlSessionTemplate.commit(force);
    }

    public static void rollback(){
        checkSqlSessionTemplate();
        sqlSessionTemplate.rollback();
    }

    public static void rollback(boolean force){
        checkSqlSessionTemplate();
        sqlSessionTemplate.rollback(force);
    }
    //-------------应用常规操作-------------
    //-----------mybatis底层资源操作
    private static final String NO_EXECUTOR_TYPE_SPECIFIED = "No ExecutorType specified";
    private static final String NO_SQL_SESSION_FACTORY_SPECIFIED = "No SqlSessionFactory specified";
    private static final String NO_SQL_SESSION_SPECIFIED = "No SqlSession specified";
    private static final ThreadLocal<SqlSession> sqlSessionCache = new ThreadLocal<SqlSession>();
    static SqlSession getSqlSession(SqlSessionFactory sessionFactory,ExecutorType executorType){
        Preconditions.checkNotNull(sessionFactory,NO_SQL_SESSION_FACTORY_SPECIFIED);
        Preconditions.checkNotNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);

        if(sqlSessionCache.get()==null){
            sqlSessionCache.set(sessionFactory.openSession(executorType));
        }
        return sqlSessionCache.get();
    }

    public static void close(){
        if(sqlSessionCache.get()==null){//上下文环境里没有会话，直接返回
            return;
        }
        SqlSession sqlSession = sqlSessionCache.get();
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Closing SqlSession [" + sqlSession + "]");
        }
        sqlSessionCache.remove();
        try{
            sqlSession.commit(true);
        }finally{
            sqlSession.close();
        }
    }

    //-----------mybatis底层资源操作-----------
}
